/*
*************************************************
				Copyagent
	------------------------------------------
	           crawler_lib.c
*************************************************
Biblioteca de functii utilizata de catre crawler
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include "crawler_lib.h"



extern char text_buffer[BUFFER_SIZE];
extern char link_buffer[BUFFER_SIZE];
extern void FSM (char arg);
extern void reset_indexes_and_buffers();



/*
urlListNode * getFirstNode(struct URLList * lista)
-------------------------------------------------
Ia primul nod din lista
-------------------------------------------------
struct URLList * lista:	Lista din care sa ia primul nod
urlListNode * return: 	Primul nod din lista
*/
urlListNode * getFirstNode(struct URLList * lista)
{
	return lista;
}


/*
urlListNode * getLastNode(struct URLList * lista)
-------------------------------------------------
Ia ultimul nod din lista
-------------------------------------------------
struct URLList * lista:	Lista al carui ultim nod trebuie returnat 
urlListNode * return: 	Ultimul nod din lista
*/
urlListNode * getLastNode(struct URLList * lista)
{
	struct URLList * ret_lista =
		(struct URLList *)malloc(sizeof(struct URLList *));
	if (lista == NULL)
    	return NULL;
	else
	{
		ret_lista = lista;
	}

	while (ret_lista->next != NULL)
		
		{
			ret_lista = ret_lista->next;
		}
	
	return ret_lista;
	
}


/*
int insertNode(char * url, struct URLList * lista)
-------------------------------------------------
Insereaza un url la sfarsitul listei
-------------------------------------------------
char* url: 		url-ul ce trebuie inserat
struct URLList* lista:	lista in care se insereaza url-ul
int return:    		0 	- succes
			!= 0 	- altfel
*/
int insertNode(char * url, struct URLList * lista)
{
	if(url == NULL)
		return 2;
	if (strlen(url) < MIN_URL_LEN)
		return 2;
	
	
	urlListNode * lastNode  = 
		(struct URLList *)malloc(sizeof(struct URLList *));
	
	
	
	lastNode = getLastNode(lista);

	urlListNode * Nod = 
		(struct URLList *)malloc(sizeof(struct URLList) + FILENAME_LENGTH);
	
	if (Nod == NULL)
	{
		perror("Eroare la alocarea unui nod\n");
		return errno;
	}
	
	Nod -> url = (char *)malloc(FILENAME_LENGTH);

	strcpy (Nod -> url, url);
	
	Nod -> next = NULL;
	Nod -> prev = lastNode;
	
	if (lastNode -> url == NULL)
	{
		memcpy(lastNode,Nod,sizeof(struct URLList) + FILENAME_LENGTH); 
		
	}
	else
	{
		lastNode -> next = Nod;
	}

	return 0;
}


/*
char * scoate_penultimul_url(struct URLList * lista);
-------------------------------------------------
Extrage ultimul url din lista si elibereaza spatiul ocupat de acel nod.
-------------------------------------------------
return char *: url-ul extras
struct URLList * lista:	lista din care se extrage url-ul
*/

char * scoate_penultimul_url(struct URLList * lista)
{
	
	if ( lista == NULL )
	{
		printf("Lista e NULL: \n");
		fflush(stdout);
		return NULL;
	}
	struct URLList * lastNode = 
			(struct URLList *)malloc(sizeof(struct URLList *));
	
	char *  url = (char *)malloc(FILENAME_LENGTH);
	

	lastNode = getLastNode(lista);
	printf("Last node a: %s\n",lastNode->url);
	fflush(stdout);

	if (lastNode == NULL)
		return NULL;
	if (lastNode->prev->parcurs == FALS)
	{	
		strcpy(url, lastNode->prev->url);
	}
	else
	{
		printf("%s parcurs = ADEVARAT\n",lastNode->prev->url);
		fflush(stdout);
		return NULL;
	}
	printf("Last node: %s\n",lastNode->url);
	fflush(stdout);
	lastNode->prev = (lastNode->prev)->prev;
	(lastNode -> prev) ->next = lastNode;
	
	return url;

}


/*
int readFile(char * filename, struct URLList * lista)
-------------------------------------------------
Citeste url-urile din fisierul de url-uri si le 
introduce in lista.
-------------------------------------------------
char * filename: 	Numele fisierului cu url-uri
struct URLList * lista:	Lista in care sa fie introduse url-urile
int return: 		0 - succes; !=0 - altfel
*/
int readFile(char * filename, struct URLList * lista)
{
	FILE * fd;
	int urlNo, i;
	char buffer[BUFFER_SIZE];

	fd = fopen(filename,"r");
	if (fd == NULL )
	{
		perror("Eroare la deschiderea fisierului\n");
		return errno;
	}
	if ( fgets(buffer, BUFFER_SIZE, fd) == NULL)
	{
		perror("Eroare la citirea primei linii a fisierului\n");
		return errno;
	}
	
	urlNo = atoi(buffer);
	for ( i = 0 ; i < urlNo ; i++)
	{
		if ( fgets(buffer, BUFFER_SIZE, fd) == NULL)
		{
			perror("Eroare la citirea primei linii a fisierului\n");
			return errno;
		}
		/*Daca ultimul caracter este linie noua, sfarseste stringul acolo*/
		if (buffer[strlen(buffer)-1] == '\n')
				buffer[strlen(buffer)-1] = '\0';
		insertNode(buffer, lista);
	}
	
	fclose(fd);

	return 0;
}

/*
void debugPrint(int node_rank, char* message)
-------------------------------------------------
Afiseaza informatie de debug de forma:
{0}Mesaj
Doar daca DEBUG este definit
-------------------------------------------------
int node_rank:	Rank-ul nodului
char* message:	Mesajul ce se va afisa
*/
void debugPrint(int node_rank, char* message)
{
#ifdef DEBUG
	printf("{%d}%s", node_rank, message);
#endif
	return;
}






/*
int replaceChars(char * string, char toReplace, char replacer);
-------------------------------------------------
Inlocuieste fiecare aparitie in sir a literei toReplace cu replacer.
-------------------------------------------------
int return:		0 daca operatiunea a avut succes
char* sir:		sirul in care se fac schimbarile
char toReplace:	litera care va fi inlocuita
char replacer:	litera care va inlocui
*/
int replaceChars(char * sir, char toReplace, char replacer)
{
	int i = 0;
	if (sir == NULL)
		return -1;
	for ( i = 0 ; i < strlen(sir) ; i++)
		if (sir[i] == toReplace)
			sir[i] = replacer;
	return 0;
}

/*
char * extrageHostDinURL(char * URL)
-------------------------------------------------
Extrage www.ploiesti.ro din http://www.ploiesti.ro/index1.php
-------------------------------------------------
return char* : 	Host
char* URL:		URL-ul din care trebuie extras hostname-ul
*/
char * extrageHostDinURL(char * URL)
{
	char * host = (char *)malloc(FILENAME_LENGTH);
	char * aux = (char *)malloc(FILENAME_LENGTH);
	char * aux2 = (char *)malloc(FILENAME_LENGTH);
	
	int hostLength = 0;
	if (URL == NULL)	
		return NULL;
	strcpy(aux,URL);

	host = strstr(aux, "http://");
	if(host == NULL)
		return NULL;
	host = host+7;
	

	aux2 = strchr(host,'/');
	
	memset(aux2, 0, FILENAME_LENGTH);	
	
	if ( aux2 != NULL)
	{
		hostLength = aux2 - host;
		aux2 = strncpy ( aux2, host, hostLength);
	}
	else
	{
		aux2 = strcpy(aux2, host);
	}	

	return aux2;
}


/*
int parse_file(char* buffer, char* url_buffer, char* prop_buffer)
-------------------------------------------------
Parseaza fisierul file_name si pune propozitiile intalnite in prop_file
si link-urile in link_file.
-------------------------------------------------
int return: 	0 daca parsarea s-a facut cu succes, !=0 altfel
char* buffer:	bufferul care va fi parsat
char * url_buffer:bufferul care va contine url-urile 
char * prop_buffer:bufferul care va contine propozitiile
*/
int parse_buffer(char* buffer, char * url_buffer, char * prop_buffer)
{

	int i = 0;	
	

	
	if (( url_buffer == NULL ) || ( prop_buffer == NULL ))
	{
		perror("Eroare la citirea bufferelor\n");
		return errno;
	}
	
	for ( i = 0 ; i < strlen (buffer) ; i++)
		FSM(buffer[i]);

	strcpy(url_buffer, link_buffer);
	strcpy(prop_buffer, text_buffer);

	reset_indexes_and_buffers();

		
	
	return 0;
}
/*
char * denumeste_link_file(char * url, int rank);
-------------------------------------------------
Denumeste fisierul de link-uri pentru url-ul dat
-------------------------------------------------
char * url_file:	numele fisierului site-ului din care se vor extrage propozitiile
int rank:	rank-ul nodului care parcurge site-ul
return char*:	numele fisierului
*/
char * denumeste_link_file(char * url_file, int rank)
{
	char * file = (char *)malloc(FILENAME_LENGTH);
	memset(file, 0 , FILENAME_LENGTH);
	sprintf(file,"%s_link_file-%d.txt",url_file, rank);
	return file;
	
}


/*
char * denumeste_prop_file(char * url, int rank);
-------------------------------------------------
Denumeste fisierul de propozitii / text pentru url-ul dat
-------------------------------------------------
char * url_file:	numele fisierului site-ului din care se vor extrage propozitiile
int rank:	rank-ul nodului care parcurge site-ul
return char*:	numele fisierului
*/
char * denumeste_prop_file(char * url_file, int rank)
{
	char *  file = (char *)malloc(FILENAME_LENGTH);
	memset(file, 0 , FILENAME_LENGTH);
	sprintf(file,"%s_prop_file-%d.txt",url_file, rank);
	return file;
	
}

/*
char * normalizez_nume_fisier(char * nume);
-------------------------------------------------
Elimina din nume caracterele care nu pot aparea intr-un nume de fisier
-------------------------------------------------
char * nume:	numele din care trebuie eliminate caracterele speciale
return char *:	numele normalizat
*/
char * normalizez_nume_fisier(char * nume)
{
	char *  file = (char *)malloc(FILENAME_LENGTH);
	strcpy(file, nume);
	replaceChars(file,'/','_');
	replaceChars(file,':','_');
	return file;

}

/*
int sparge_prop_bufferul(char * buffer, FILE * dest_file);
-------------------------------------------------
Sparge bufferul in unitati de propozitii.
Separarea se face in functie de US - unit separator.
Unitatile rezultate se pun pe cate o linie noua in fisierul destinatie.
Rezultatul este 0 daca spargerea s-a facut cu succes. Diferit de 0 atlfel.
-------------------------------------------------
char * buffer:	buffer-ul de spart
FILE * dest_file:	fisierul destinatie
return int:	0 daca totul a fost in regula. !=0 altfel
*/
int sparge_prop_bufferul(char * buffer, FILE * dest_file)
{

  char * unit;
  char * tokeni = (char *)(malloc(2));
  sprintf(tokeni,"%c.",US);
	
  unit = strtok (buffer,tokeni);
  while (unit != NULL)
  {
  	if  (strlen (unit) > LUNG_MIN_PROP)
	{
		
    	fprintf (dest_file,"|||%s|||\n",unit);
	}
    unit = strtok (NULL,tokeni);
  }
  return 0;
}

/*
int sparge_link_bufferul(char * buffer,  char * url_curent, struct URLList * lista);
-------------------------------------------------
Sparge bufferul in unitati de linkuri.
Separarea se face in functie de US - unit separator.
Unitatile rezultate se pun pe cate o linie noua in fisierul destinatie si in lista 
de url-uri.
Daca link-ul este relativ, se va concatena la url_curent rezultand un link absolut
Rezultatul este 0 daca spargerea s-a facut cu succes. Diferit de 0 atlfel.
-------------------------------------------------
char * buffer:	buffer-ul de spart
char * url_curent:	url-ul paginii parsate
struct URLList * lista:	lista cu urluri la care se vor adauga url-urile noi
return int:	0 daca totul a fost in regula. !=0 altfel
*/
int sparge_link_bufferul
	(char * buffer,  char * url_curent, struct URLList * lista)
{

  char * unit;
  char * tokeni = (char *)(malloc(2));
  char * url_absolut = (char *)(malloc(FILENAME_LENGTH));

  sprintf(tokeni,"%c",US);

	
  unit = strtok (buffer,tokeni);
  while (unit != NULL)
  {
  
/*
	
	De adaugat suport pentru linkurile relative
  	if (strstr(unit,"http://") ==NULL)
	{
		
		url_absolut = adauga_rel_la_abs(url_curent, unit);
    
	}
	else
	{
		printf("A\n");
		strcpy(url_absolut, unit);
	}
	if ( (url_absolut != NULL) && (extensia_e_valida (url_absolut) == 1) )
		{
			fprintf (dest_file,"|||%s|||\n",url_absolut);
			//insertNode(url_absolut, lista);
		}
		*/
	  if ((strstr(unit,"http://") != NULL) &&  (extensia_e_valida (unit) == ADEVARAT)   
		      && (strchr(unit,'%') == NULL ) )
		{
			memset(url_absolut,0,FILENAME_LENGTH);
			//printf("Canonizeaza: %s \n", unit);
			//fflush(stdout);
			url_absolut = canonizeaza_url(unit);
			/*
				Urmatoarea conditie este foarte importanta. Trebuie insa gasita o varianta 
				mai eleganta.
			*/
			//printf("Canonizat: %s \n", url_absolut);
			//fflush(stdout);
			if (url_absolut == NULL)
			{
				break;
			}			
				
			//printf ("|||%s|||\n",url_absolut);
			//fflush(stdout);
			if (e_url_unic(url_absolut, lista) == ADEVARAT)
				insertNode(url_absolut, lista);

			
			
		}
    unit = strtok (NULL,tokeni);
  }
  return 0;
}

/*
int extensia_e_valida(char * url);
-------------------------------------------------
Valideaza extensia
-------------------------------------------------
char * url:	url-ulcare trebuie validat
int return:	0 daca e invalid, 1 daca e valida
*/
int extensia_e_valida(char * url)
{
	int i = 0;
	int nr_extensii = 3;
	char * aux_url = (char *)(malloc(FILENAME_LENGTH));
	char *extensii[] = {".php",".html",".htm"};
	
	aux_url = strrchr(url, '.');
	if ( aux_url == NULL)
		return 0;
	
	for ( i = 0 ; i < nr_extensii ; i++)
	{
		if (strstr(aux_url, extensii[i]) != NULL )
			return 1;
	}
	return 0;
}

/*
char * adauga_rel_la_abs(char * absolut, char * relativ);
-------------------------------------------------
Adauga un link relativ la unul absolut
-------------------------------------------------
char * absolut:	url-ul absolut
char * relativ:	url-ul relativ
return char *:	rezultatul
*/
char * adauga_rel_la_abs(char * absolut, char * relativ)
{
	char * result = (char *)(malloc(FILENAME_LENGTH));
	char * abs_aux  = (char *)(malloc(FILENAME_LENGTH));
	char * aux;
	int final_slash = 0;
	int starting_slash = 0;
	int last_index_of_slash = 0;
	
	if (strstr(relativ,"mailto:") != NULL)
		return NULL;
	
	aux = strrchr(absolut,'/');
	last_index_of_slash = aux - absolut;
	if ( last_index_of_slash > 6 )
		strncpy(abs_aux, absolut, last_index_of_slash );
	
	if (absolut[strlen(abs_aux) - 1] == '/') 
		final_slash = 1;
	else
		final_slash = 0;
		
	if (relativ[0] == '/') 
		starting_slash = 1;
	else
		starting_slash = 0;
		
	if ((final_slash == 1) && (starting_slash == 1))
		sprintf(result,"%s%s",abs_aux, relativ );
	else
		if ((final_slash == 0) && (starting_slash == 0))
			sprintf(result,"%s/%s",abs_aux, relativ );
		else
			sprintf(result,"%s%s",abs_aux, relativ );
	
	return result;
		
	
}

/*
int contine_caractere_nepermise(char * url);
-------------------------------------------------
Verifica daca url-ul contine caractere nepermise
-------------------------------------------------
return int: ADEVARAT / FALS
char * url: url-ul ce va fi verificat
*/
int contine_caractere_nepermise(char * url)
{
	int i = 0;

		for ( i = 0 ; i < strlen(url) ; i++)
		{
			if( isspace(url[i]) != FALS )
				return ADEVARAT;
			
			if( isalnum(url[i]) == FALS )
				if (
					!( 
					(url[i] == '/') || 
					(url[i] == '?') || 
					(url[i] == '+') || 
					(url[i] == '-') || 
					(url[i] == '_') || 
					(url[i] == '.') || 
					(url[i] == ':') || 
					(url[i] == ';') || 
					(url[i] == '!')	|| 
					(url[i] == '&') || 
					(url[i] == '=') 
					)
				  )
				{
					return ADEVARAT;
				}
		}
		return FALS;


}

/*
char * canonizeaza_url(char * url);
-------------------------------------------------
Pune un '/' la sfarsitul url-ului daca acest e de forma http://www.prestez.com.
Devenind: http://www.prestez.com/
-------------------------------------------------
char * url:	url-ul de canonizat;
return char *: url-ul canonizat
*/
char * canonizeaza_url(char * url)
{
	
	char * host = (char *)malloc(FILENAME_LENGTH);
	char * aux = (char *)malloc(FILENAME_LENGTH);
	char * aux2 = (char *)malloc(FILENAME_LENGTH);
	char * ret = (char *)malloc(FILENAME_LENGTH);
	
	
	
	
	if (url == NULL)	
		return NULL;
	if (strlen(url) > 255)	
	{
		printf("%s e prea lung\n", url);
		fflush(stdout);
		return NULL;
	}
	
	strcpy(aux,url);


	host = strstr(aux, "http://");
	if ((host == NULL) || (host != aux))
		return NULL;
	host = host+7;

	//printf("contine: [%s]\n", url);
	//fflush(stdout);

	if ( contine_caractere_nepermise(url) == ADEVARAT )
	{
		printf("%s nu e e bun\n", url);
		fflush(stdout);
		return NULL;
	}
	
	aux2 = strchr(host,'/');
	if (aux2 == NULL)
		sprintf(ret, "%s/",url);
	else
	{
		strcpy(ret, url);
	}
	
	return ret;
} 

/*
void afiseaza_lista_url(struct URLList * lista);
-------------------------------------------------
Afiseaza lista
-------------------------------------------------
struct URLList * lista: lista ce va fi afisata
*/
void afiseaza_lista_url(struct URLList * lista)
{
	struct URLList * nod;
	if (lista == NULL)
		return;
	nod = lista;
	printf("=====Lista=====\n");
	do
	{
		printf ("%s\n",nod->url);
		nod = nod->next;
	}
	while(nod != NULL);
	printf("===============\n");
	return;
}

/*
struct status_eroare relativ_din_absolut(char * absolut, char * domeniu, char * relativ);
-------------------------------------------------
Sparge un link in partea sa relativa si in domeniu
ex: www.prestez.com/servicii -> www.prestez.com si /servicii
www.prestez.com	-> /
-------------------------------------------------
return struct status_eroare: starea la terminare
char * absolut:	link-ul care trebuie prelucrat
char * domeniu: partea de domeniu a link-ului
char * relativ: partea de dupa domeniu - relativa
*/
Status_eroare relativ_din_absolut(char * absolut, char * domeniu, char * relativ)
{
	
	struct status_eroare status;

	char * host = (char *)malloc(FILENAME_LENGTH);
	char * aux = (char *)malloc(FILENAME_LENGTH);
	char * aux2 = (char *)malloc(FILENAME_LENGTH);
	char * dom = (char *)malloc(FILENAME_LENGTH);
	char * rel = (char *)malloc(FILENAME_LENGTH);
	memset(dom, 0, FILENAME_LENGTH);
	
	
	if (absolut == NULL)	
	{
		strcpy(status.mesaj,"relativ_din_absolut: Url absolut e NULL");
		status.eroare = ADEVARAT;
		return status;
	}
	
		
	strcpy(aux,absolut);

	host = strstr(aux, "http://");
	if ((host == NULL) || (host != aux))
	{
		strcpy(status.mesaj,"relativ_din_absolut:");
		status.eroare = ADEVARAT;
		return status;
	}
	host = host+7;
	
/*
Cazuri posibile
1.) http://www.era.ro
2.) http://www.era.ro/
3.) http://www.era.ro/pagina.html/ sau http://www.era.ro/pagina.html
*/
	aux2 = strchr(host,'/');
	if (aux2 == NULL)
	{
		/*cazul 1.) */
		sprintf(rel, "/");
		sprintf(dom,host);
	}
	else
	{
		if (strlen(host) == (aux2 - host + 1))
		{
			/*cazul 2.) */
			sprintf(rel, "/");
			strncpy(dom, host, strlen(host) -1);
		}
		else
		{
			/*cazul 3.) */
			strncpy(dom, host, aux2 - host);
			host = host + (aux2 - host + 1);
			sprintf(rel,"/%s", host);
		}
	}

	strcpy(domeniu, dom);
	strcpy(relativ, rel);
	return status;
	

}
/*
int e_url_unic_local(char * url)
-------------------------------------------------
Verifica da ca url-ul se afla in lista proprie a nodului
-------------------------------------------------
return int: ADEVARAT sau FALS
char * url: url-ul de cautat
struct URLList * lista: lista in care se cauta
*/
int e_url_unic_local(char * url, struct URLList * lista)
{
	struct URLList * nod =
			(struct URLList *)malloc(sizeof(struct URLList *));
	nod = lista;
	while(nod)
	{
		if (strcmp(nod->url,url) == 0)
		{
			return FALS;
		}
		nod = nod->next;
	}
	return ADEVARAT;
}

/*
int e_url_unic_global(char * url)
-------------------------------------------------
Verifica da ca url-ul se afla in lista altor
-------------------------------------------------
return int: ADEVARAT sau FALS
char * url: url-ul de cautat
*/
int e_url_unic_global(char * url)
{
	/*
	if(my_rank == 0)
	{
	for (i = 0 ; i  < waiting_size ; i++)
	MPI_Recv(mesaj);
		
}
	else
	MPI_Send(mesaj);

	MPI_Bcast(mesaj, root = 0);

	for (i = 0 ; i  < waiting_size ; i++)
	MPI_Recv(mesaj);

	*/


}
/*
int e_url_unic(char * url);
-------------------------------------------------
Verifica daca un url se mai afla in vreo lista
-------------------------------------------------
return int: ADEVARAT sau FALS
char * url: url-ul de cautat
struct URLList * lista: lista locala

*/
int e_url_unic(char * url,  struct URLList * lista)
{
	if (e_url_unic_local(url, lista) == FALS)
		return FALS;
	else return ADEVARAT;
	//else
		//return e_unic_global(url);
}



